package com.wimcorp.magic.dataloader.server;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.wimcorp.magic.data.AbilityData;
import com.wimcorp.magic.data.BehaviourData;
import com.wimcorp.magic.data.CardData;
import com.wimcorp.magic.data.CostData;
import com.wimcorp.magic.data.DeckData;
import com.wimcorp.magic.data.dao.AbilityDataDao;
import com.wimcorp.magic.data.dao.BehaviourDataDao;
import com.wimcorp.magic.data.dao.CardDataDao;
import com.wimcorp.magic.data.dao.CostDataDao;
import com.wimcorp.magic.data.dao.DeckDataDao;
import com.wimcorp.magic.dataloader.client.DataService;
import com.wimcorp.magic.dataloader.dto.CardDTO;
import com.wimcorp.magic.dataloader.dto.DeckDTO;

/**
 * The server-side implementation of the RPC service.
 */
@Configurable
@SuppressWarnings("serial")
public class DataServiceImpl extends RemoteServiceServlet implements DataService {

	@Autowired
	private CardDataDao cardDataDao;

	@Autowired
	private AbilityDataDao abilityDataDao;

	@Autowired
	private BehaviourDataDao behaviourDataDao;

	@Autowired
	private DeckDataDao deckDataDao;

	@Autowired
	private CostDataDao costDataDao;

	@Override
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		ApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
		AutowireCapableBeanFactory factory = context.getAutowireCapableBeanFactory();
		factory.autowireBean(this);
		System.out.println(Thread.currentThread().getContextClassLoader()
				.getResource("org/externalmodel/MyReallyValidClass.class"));
	}

	@Override
	public CardDTO loadCard(long multiverseid) {
		CardData card = cardDataDao.getCardData(380236);
		return new CardDTO(card.getAbilities(), card.getBehaviours(), card.getTypes(), card.getColors(),
				card.getMultiverseid(), card.getName(), card.getSubtypes(), card.getPower(), card.getToughness(),
				card.getManacost(), card.getText(), card.getCount(), card.getNumber(),card.isVerified());

	}

	@Override
	public List<DeckDTO> getDeckNames() {
		List<DeckDTO> deckNames = new ArrayList<DeckDTO>();
		List<DeckData> decks = deckDataDao.getDeckNames();
		for (DeckData deck : decks) {
			deckNames.add(new DeckDTO(deck.getCode(), deck.getName()));
		}
		return deckNames;
	}

	@Override
	public List<CardDTO> getCards(String code) {
		List<CardDTO> cardData = new ArrayList<CardDTO>();
		List<CardData> cards = cardDataDao.getCards(code);
		for (CardData card : cards) {
			cardData.add(new CardDTO(card.getAbilities(), card.getBehaviours(), card.getTypes(), card.getColors(), card
					.getMultiverseid(), card.getName(), card.getSubtypes(), card.getPower(), card.getToughness(), card
					.getManacost(), card.getText(), card.getCount(), card.getNumber(),card.isVerified()));
		}
		return cardData;
	}

	@Override
	public void saveCard(CardDTO cardDTO) {
		CardData dbCard = cardDataDao.getCardData(cardDTO.getMultiverseid());
		Set<BehaviourData> behaviours = new HashSet<>();
		for (BehaviourData behaviourData : cardDTO.getBehaviours()) {
			BehaviourData behaviour = behaviourDataDao.getBehaviour(behaviourData.getBehaviour());
			if (behaviour == null) {
				behaviourDataDao.saveOrUpdate(behaviourData);
				behaviour = behaviourData;
			}
			behaviours.add(behaviour);
		}

		dbCard.setBehaviours(behaviours);

		Set<AbilityData> resultAbilities = new HashSet<>();
		for (AbilityData dtoAbility : cardDTO.getAbilities()) {
			Set<CostData> resultCosts = new HashSet<>();
			Set<AbilityData> cardAbilities = dbCard.getAbilities();
			if (cardAbilities.size() == 0) {
				abilityDataDao.saveOrUpdate(dtoAbility);
				for (CostData costData : dtoAbility.getCosts()) {
					costDataDao.saveOrUpdate(costData);
					resultCosts.add(costData);
				}
				dtoAbility.setCosts(resultCosts);
				abilityDataDao.saveOrUpdate(dtoAbility);
				resultAbilities.add(dtoAbility);
			} else {
				boolean found = false;
				for (AbilityData dbAbility : cardAbilities) {
					if (dbAbility.getAbility().equals(dtoAbility.getAbility())) {
						dbAbility.setConfig(dtoAbility.getConfig());
						boolean foundCost = false;
						Set<CostData> costs = dbAbility.getCosts();

						for (CostData dtoCost : dtoAbility.getCosts()) {

							for (CostData dbCost : costs) {

								if (dbCost.getCost().equals(dtoCost.getCost())) {
									dbCost.setConfig(dtoCost.getConfig());
									costDataDao.saveOrUpdate(dtoCost);
									resultCosts.add(dtoCost);
									foundCost = true;
								}
							}
							if (!foundCost) {
								costDataDao.saveOrUpdate(dtoCost);
								resultCosts.add(dtoCost);
							}

						}
						dbAbility.setCosts(resultCosts);
						abilityDataDao.saveOrUpdate(dbAbility);
						resultAbilities.add(dbAbility);
						found = true;
						
						/*for (CostData dbCost : costs) {
							// check if we need to delete this cost
							boolean deleteCost = true;
							for (CostData dtoCost : dtoAbility.getCosts()) {
								if (dbCost.getCost().equals(dtoCost.getCost())) {
									deleteCost = false;
								}
							}
							if (deleteCost) {
								costDataDao.delete(dbCost);
							}
						}*/
					}
					// check if we need to delete this ability
					/*boolean delete = true;
					for (AbilityData dtoAbility2 : cardDTO.getAbilities()) {
						if (dbAbility.getAbility().equals(dtoAbility2.getAbility())) {
							delete = false;
						}
					}
					if (delete) {
						abilityDataDao.delete(dbAbility);
					}*/
				}
				if (!found) {
					dtoAbility.setCosts(resultCosts);
					abilityDataDao.saveOrUpdate(dtoAbility);
					resultAbilities.add(dtoAbility);
				}
			}

		}
		dbCard.setAbilities(resultAbilities);
		Set<CostData> costs = new HashSet<>();
		for (CostData costData : cardDTO.getCosts()) {
			Set<CostData> cardCosts = dbCard.getCosts();
			if (cardCosts.size() == 0) {
				costDataDao.saveOrUpdate(costData);
				costs.add(costData);
			} else {
				boolean found = false;
				for (CostData c : cardCosts) {
					if (c.getCost().equals(costData.getCost())) {
						c.setConfig(costData.getConfig());
						costDataDao.saveOrUpdate(c);
						costs.add(c);
						found = true;
					}
				}
				if (!found) {
					costDataDao.saveOrUpdate(costData);
					costs.add(costData);
				}
			}

		}
		dbCard.setCosts(costs);

		dbCard.setVerified(cardDTO.isVerified());
		cardDataDao.saveOrUpdate(dbCard);
	}

}
